package com.woniuxy.sddfp.system.service.impl;
import java.math.BigDecimal;

import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.woniuxy.sddfp.common.exception.Assert;
import com.woniuxy.sddfp.common.model.dto.user.LoginDto;
import com.woniuxy.sddfp.common.model.dto.user.UserRegisterDto;
import com.woniuxy.sddfp.common.model.vo.user.LoginVo;
import com.woniuxy.sddfp.common.result.enums.impl.BusinessCode;
import com.woniuxy.sddfp.system.entity.User;
import com.woniuxy.sddfp.system.entity.UserDetail;
import com.woniuxy.sddfp.system.mapper.UserMapper;
import com.woniuxy.sddfp.system.service.IUserDetailService;
import com.woniuxy.sddfp.system.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.Objects;
import java.util.UUID;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author yellow doctor
 * @since 2025-05-06
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private IUserDetailService userDetailService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     *   1、判断 这个手机号码是否已注册
     *   2、添加user对象
     *   3、添加userDetail对象
     *
     *   事务失效的几种场景
     *   1、当前的方法是 public修饰
     *   2、当前的方法是 final 修饰
         3、非事务方法调用事务方法
         4、try-catch 捕获 异常
         5、抛出的异常范围太大 (默认只能处理 RuntimeException )
     * @param userRegisterDto
     */
    @Transactional
    @Override
    public void register(UserRegisterDto userRegisterDto) {

        // getOne() 方法从哪里来的 ?   父类来的
        // Wrappers.lambdaQuery(User.class)
        // LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class);
        // (User::getPhone, userRegisterDto.getPhone())  获取的是属性字段、查询条件
        //1、判断 这个手机号码是否已注册
        User user = getOne(Wrappers.lambdaQuery(User.class)
                .eq(User::getPhone, userRegisterDto.getPhone()));

//        if (user != null) {
//            //抛出异常 phone 已经被注册
//            Assert.error(BusinessCode.USER_EXISTED);
//        }
        //判断 对象为空   如何判断
        //  user != null
        //使用工具类   Objects  java api提供的 工具
//        Objects.isNull()
//        Objects.nonNull()
//        ObjectUtils.isEmpty()  spring 提供的工具
        //判断 字符串为空 如何判断
//        String a = "";  String b = null;
//        if (!a.equals("") && a != null) {
//
//        }
//        StringUtils.isEmpty(a);
//        StringUtils.hasText(a);

        //判断 list为空   如何判断
        // list.size = 0 和  list = null;
//        CollectionUtils.isEmpty();

        Assert.error(Objects.nonNull(user),BusinessCode.USER_EXISTED);

        registerUser(userRegisterDto);
    }

    private void registerUser(UserRegisterDto userRegisterDto) {
        //2、添加user对象
        User u = new User();
        u.setUserName(userRegisterDto.getUserName());
        u.setPhone(userRegisterDto.getPhone());

        //加密
//        String hashpw = BCrypt.hashpw();
//        //密码判断
////        BCrypt.checkpw()
        u.setPwd(userRegisterDto.getPwd());

        u.setUserType("1");
        //其他的字段 没有设置 后续在其他的业务中进行设置
        save(u);  //添加完毕后  user 自动有id

        //3、添加userDetail对象
        UserDetail userDetail = new UserDetail();
        userDetail.setId(u.getId());
        userDetail.setUserName(userRegisterDto.getUserName());
        userDetail.setPhone(userRegisterDto.getPhone());
        userDetail.setBorrowMoney(new BigDecimal("0"));
        //用户码 随机数   UUID.randomUUID().toString().substring(0,12) 截取 12 位
        userDetail.setUserCode("UC_" + UUID.randomUUID().toString().substring(0,12));
        //请求码 其他地方修改
//        userDetail.setReqCode("");
        //是否绑卡 默认未绑卡 false
        userDetail.setCardBindStatus(false);

        userDetailService.save(userDetail);
    }

    /**
     * 登录业务实现
     * @param loginDto
     * @return
     */
    @Override
    public LoginVo login(LoginDto loginDto) {

        LoginVo loginVo = loginDto.getIsPhoneLogin() ? loginByPhone(loginDto):loginByUsername(loginDto);

        return loginVo;
    }

    /**
     * 用户名和密码登录
     * @param loginDto
     * @return
     */
    private LoginVo loginByUsername(LoginDto loginDto) {

        //1、通过 username 查询 用户
        User user = getOne(Wrappers.lambdaQuery(User.class).eq(User::getUserName, loginDto.getLoginName()));
        Assert.error(Objects.isNull(user),BusinessCode.USER_NOT_EXIST);

        //2、判断密码
        Assert.error(!user.getPwd().equals(loginDto.getLoginSecret()),BusinessCode.USER_LOGIN_ERROR);

        //3、返回loginVo
        return createLoginVo(user);
    }

    private LoginVo createLoginVo(User user) {

        //set 操作属性
        LoginVo LoginVo1 = new LoginVo();
        LoginVo1.setId(user.getId());
        LoginVo1.setUserName(user.getUserName());
        LoginVo1.setPhone(user.getPhone());
        LoginVo1.setToken("");

        //@Builder  这个注解 可以使用 链式编程  的方法 操作属性 (装X)
        LoginVo loginVo = LoginVo.builder()
                .id(user.getId())
                .phone(user.getPhone())
                .userName(user.getUserName())
                .token("")
                .build();

        return loginVo;
    }

    /**
     * 手机号码登录
     * @param loginDto
     * @return
     */
    private LoginVo loginByPhone(LoginDto loginDto) {

        //01 登录
        //校验验证码
        String codeKey = "user:" + loginDto.getLoginName() + ":" + loginDto.getLoginSecret();
        String redisCode = stringRedisTemplate.opsForValue().get(codeKey);

        //验证码过期
        Assert.error(StringUtils.isEmpty(redisCode),BusinessCode.USER_TIMEOUT_CODE_ERROR);
        //验证码错误
        Assert.error(!loginDto.getLoginSecret().equals(redisCode),BusinessCode.USER_VERIFY_CODE_ERROR);

        //判断用户是否存在  如果存在就直接 登录
        //                 不存在 注册 登录  (所有的都是有这个功能)
        //如果是 手机号码登录 getLoginName 就是phone
        //如果是 账号登录     getLoginName 就是username
        User user = getOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, loginDto.getLoginName()));

        if (user == null){
            //注册
            UserRegisterDto userRegisterDto = new UserRegisterDto();
            //随机字符串
            userRegisterDto.setUserName("user_" + UUID.randomUUID().toString().substring(0,5));
            userRegisterDto.setPwd("123456");
            userRegisterDto.setPhone(loginDto.getLoginName());
            registerUser(userRegisterDto);

            //返回一个 user 查询刚刚注册的 返回给前端
            user = getOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, loginDto.getLoginName()));
            return   createLoginVo(user);
        }
        LoginVo loginVo = createLoginVo(user);

        return loginVo;
    }
}
